# -*- coding: utf-8 -*-
# Copyright 2011-2012 MagiCycles.com
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


import logging
from datetime import datetime

from google.appengine.api import memcache

from django.utils import simplejson

from datastore.common import CommentCategory
from datastore.people import Comment
from datastore.routes import Route, Ride

from common import *
from common import _S,_US,Box,Point,unescape

import people_api

def _Route_Encoder(route):

    if isinstance(route, Route):
        return {"id": route.id,
                "name": unescape(route.name),
                "location": _S(route.location),
                "start_address": unescape(route.start_address),
                "end_address": unescape(route.end_address),
                "start_location": _S(route.start_location),
                "end_location": _S(route.end_location),
                "distance": route.distance,
                "create_datetime": route.create_datetime,#.strftime("%Y-%m-%d"),
                "preview_url": unescape(route.preview_url),
                "creator": unescape(route.creator.name),
                "creator_uid": unescape(route.creator.uid),
                "directions_store": route.directions_store.id,
                "city": unescape(route.city),
                "state": unescape(route.state),
                "country": unescape(route.country),
                "rides_count": route.Stats.rides_count,
                "fav_count": route.Stats.fav_count,
                "route_type": route.route_type,
                "file_url": route.file_url,
                }
    elif isinstance(route, datetime):
        return route.isoformat()
    else:
        raise TypeError('%r is not JSON serializable' % route)

def _Comment_Encoder(comment):

    if isinstance(comment, Comment):
        return {"id": comment.id,
                "routeid": comment.route.id,
                "location": _S(comment.location),
                "posted_on": comment.posted_on,
                "photourl": unescape(comment.photourl),
                "reviewer": unescape(comment.reviewer.name),
                "reviewer_photourl": unescape(comment.reviewer.photourl),
                "reviewer_uid": unescape(comment.reviewer.uid),
                "review": unescape(comment.review),
                "category": comment.category,
                "route_id": comment.route.id,
                "route_name": unescape(comment.route.name),
                }
    elif isinstance(comment, datetime):
        return comment.isoformat()
    else:
        raise TypeError('%r is not JSON serializable' % comment)

def _Ride_Encoder(obj):
    if isinstance(obj, Ride):
        return {"id": obj.id,
                "rider": obj.rider.name,
                "rider_uid": obj.rider.uid,
                "bike": obj.bike.name,
                "bike_id": obj.bike.id,
                "route": obj.route.name,
                "route_id": obj.route.id,
                "name": obj.name,
                "create_datetime": obj.create_datetime,
                "update_datetime": obj.update_datetime,
                "start_datetime": obj.start_datetime,
                "end_datetime": obj.end_datetime,
                "duration": obj.duration,
                "weather": obj.weather,
                "effort": obj.effort,
                "notes": obj.notes,
                }
    elif isinstance(obj, datetime):
        return obj.isoformat()
    else:
        raise TypeError('%r is not JSON serializable' % obj)

def create_route(request, name, start_address, end_address,
                start_location,end_location,total_distance,direction_result,preview_url=None,
                route_type="gmap", file_url=""):
    """ Create a route.
    Args: name, start_address, end_address,
          start_location,end_location,total_distance,
          direction_result,preview_url,route_type,file_url
    Returns:
      routeid: id of route.
    """
    user = people_api.get_current_user()
    if not user:
        user = people_api.get_anonymous_user()
    start_loc = _US(simplejson.loads(unescape(start_location)))
    end_loc = _US(simplejson.loads(unescape(end_location)))
    route = Route.create(name, start_address, end_address, start_loc, end_loc,
                long(total_distance), user, unescape(direction_result),preview_url,
                route_type, file_url)

    result = {"status": "ok",
              "routeid": route.id}
    return result

@check_site_user
def remove_route(request, routeid):
    """ Remove a route
    Args:
        routeid: routeid
    Retures:
        routeid: id of route removed
    """
    result = {"status": "error"}
    route = Route.get_by_id(long(routeid))
    if route is None:
        result["message"] = "can not get route by id %s"%routeid
        return result
    user = people_api.get_current_user()
    if user != route.Creator:
        result["message"] = "you can not remove other's route"
        return result

    route.update_attr(deleted=True)

    result["status"] = "ok"
    result["routeid"] = route.id

    return result

def get_by_id(request, routeid):
    """return route by id.
    Args: 
      routeid: route id
    Returns:
      route: json serialized str of a route.
    """
    routeid = long(routeid)
    result = {"status": "error"}
    cache_key = "get_route_by_id%s"%(routeid)
    route = memcache.get(cache_key)
    if route is None:
        route = Route.get_by_id(routeid)
        memcache.set(cache_key, route, SIGNEL_ROUTE_MEMCACHE_TIME)
        
    if route:
        result["status"] = "ok"
        result["route"] = route
        result["custom_encoder"] = _Route_Encoder
    else:
        result["message"] = "can not get route by id %s"%routeid
    return result
    
def get_routes_by_center(request, center, max_results=1000, max_distance=20000):
    """return routes around the center.
    Args: 
      center: serialized str of a gmap LatLng
      max_results: max count of items return 
      max_distance: search around distance(unit: m)
    Returns:
      routes: list(route), list of json serialized routes.
    """
    max_results = long(max_results)
    max_distance = long(max_distance)
    center = _US(simplejson.loads(unescape(center)), pointtype = Point)
    cache_key = "get_routes_by_center_%s_%s_%s"%(center, max_results, max_distance)
    routes = memcache.get(cache_key)
    if routes is None:
        routes = Route.search_by_center(center, max_results, max_distance)
        routes = [_Route_Encoder(route) for route in routes if route is not None]
        memcache.set(cache_key, routes, ROUTES_MEMCACHE_TIME)

    result = {"status": "ok",
              "routes": routes,
              "custom_encoder": _Route_Encoder}
    return result

def get_routes_count_by_center(request, center, max_distance=20000):
    """return number of routes by center.
    Args:
      center: serialized str of a gmap LatLng
      max_distance: search around distance(unit: m)
    Returns:
      count: int, number of routes in bounds.
    """
    max_distance = long(max_distance)
    center = _US(simplejson.loads(unescape(center)), pointtype = Point)
    cache_key = "get_routes_count_by_center_%s_%s"%(center, max_distance)
    count = memcache.get(cache_key)
    if count is None:
        count = Route.count_by_center(center, max_distance)
        memcache.set(cache_key, count)

    result = {"status": "ok",
              "count": count,
              }
    return result

def get_routes_by_bounds(request, north, east, south, west, max_results=1000):
    """return routes in bounds.
    Args:
      north, east, south, west: bounds of google map
      max_results: max count of items return
    Returns:
      routes: list(route), list of json serialized routes.
    """
    max_results = long(max_results)
    bounds = Box(float(north),float(east),float(south),float(west))
    cache_key = "get_routes_by_bounds_%s_(N:%.1f, E:%.1f, S:%.1f, W:%.1f)"%(max_results,
                                                                    bounds.north, bounds.east,
                                                                    bounds.south, bounds.west)
    routes = memcache.get(cache_key)
    if not routes:
        routes = Route.search_by_bounds(bounds, max_results)
        routes = [_Route_Encoder(route) for route in routes if route is not None]
        memcache.set(cache_key, routes, ROUTES_MEMCACHE_TIME)

    result = {"status": "ok",
              "routes": routes,
              "custom_encoder": _Route_Encoder}
    return result

def get_routes_count_by_bounds(request, north, east, south, west):
    """return number of routes in bounds.
    Args:
      north, east, south, west: bounds of google map
    Returns:
      count: int, number of routes in bounds.
    """
    bounds = Box(float(north),float(east),float(south),float(west))
    cache_key = "get_routes_count_by_bounds_(N:%.1f, E:%.1f, S:%.1f, W:%.1f)"%(bounds.north, bounds.east,
                                                                               bounds.south, bounds.west)
    count = memcache.get(cache_key)
    if count is None:
        count = Route.count_by_bounds(bounds)
        memcache.set(cache_key, count)

    result = {"status": "ok",
              "count": count,
              }
    return result

def get_directions_by_id(request, id):
    """return google DirectionsResult.
    Args:
      id: directions id
    Returns:
      directions: serialized str of google map DirectionsResult.
    """
    id = long(id)
    cache_key = "get_directions_by_id_%s"%(id)
    directions = memcache.get(cache_key)
    if not directions:
        directions = Route.get_directions_by_id(id)
        if directions:
            memcache.set(cache_key, directions, SIGNEL_ROUTE_MEMCACHE_TIME)

    if directions:
        result = {"status": "ok",
                  "directions": unescape(directions),
                  }
    else:
        result = {"status": "error",
                  "message": "can not get directions by id %s"%(id),
                  }
    return result

def get_all_routes(request, pagesize=100, start_cursor=None, order='-create_datetime'):
    """return all routes.
    Args:
      pagesize: size of results
      start_cursor: start cursor positon for this search
      order: ordering for the results
    Returns:
      end_cursor: cursor position for next search,
      routes: list(route), list of json serialized routes.
    """
    pagesize = long(pagesize)
    routes,end_cursor = Route.get_all(pagesize, start_cursor, order)
    return {"status": "ok",
             "end_cursor": end_cursor,
             "routes": routes,
             "custom_encoder": _Route_Encoder}

def get_routes_by_uid(request, uid, pagesize=50, start_cursor=None, order='-create_datetime'):
    """return all routes.
    Args:
      uid: user's login account
      pagesize: size of results
      start_cursor: start cursor positon for this search
      order: ordering for the results
    Returns:
      end_cursor: cursor position for next search,
      routes: list(route), list of json serialized routes.
    """
    siteuser = people_api.get_user_by_uid(uid)
    if not siteuser:
        return {"status": "error",
                 "routes": [],
                 "message": "can not found user %s"%uid,
                 }

    pagesize = long(pagesize)
    routes,end_cursor = Route.get_routes_by_user(siteuser, pagesize, start_cursor, order)
    return {"status": "ok",
             "end_cursor": end_cursor,
             "routes": routes,
             "custom_encoder": _Route_Encoder}

def get_comments_by_route_id(request, routeid):
    """get route's comments
    Args:
      routeid: route id
    Returns:
      comments: list of comments.
    """
    routeid = long(routeid)
    return {"status": "ok",
            "comments": Comment.get_comments_by_route_id(routeid),
            "custom_encoder": _Comment_Encoder}

@check_site_user
def add_comment(request, routeid, review, location, photourl="", category=CommentCategory.text):
    """ add a comment for route by id
    Args:
      routeid: route id
      review: review content
      location: serialized str of a gmap LatLng
      photourl: photo in review
      category: category of comment. text,photo,food,lodging or sights
    Returns:
      comment: a comment
    """
    routeid = long(routeid)
    route = Route.get_by_id(routeid)
    if not route:
        return {"status": "error",
                "message": "route not found"}
    reviewer = get_current_user()
    review = review
    if len(review) > MAX_COMMENT_LENGTH:
        return {"status": "error",
                "message": "comment length exceed %d"%MAX_COMMENT_LENGTH}

    if category == CommentCategory.text and photourl:
        category = CommentCategory.photo

    location = _US(simplejson.loads(unescape(location)))
    comment = Comment.create_comment(reviewer, route, review, location, photourl, category)
    if comment:
        return {"status": "ok",
                "comment": comment,
                "custom_encoder": _Comment_Encoder}

    return {"status": "error",
            "message":"create comment error"}

@check_site_user
def save_comment_location(request, commentid, newlocation):
    """ save comment at new location
    Args:
      commentid: comment id
      newlocation: serialized str of a gmap LatLng
    Returns:
      comment: a comment
    """
    cur_user = get_current_user()
    commentid = long(commentid)

    comment = Comment.get_by_id(commentid)
    if comment.Reviewer != cur_user:
        return {"status": "error",
                "message": "you can not move other's comment"}

    newlocation = _US(simplejson.loads(unescape(newlocation)))
    comment.relocation(newlocation)
    return {"status": "ok",
            "comment": comment,
            "custom_encoder": _Comment_Encoder}


def log_a_ride(request, routeid, bikeid, starttime, endtime, duration,
               name="", weather="", effort=0, notes=""):
    """ log a ride
    Args:
      routeid: route id
      bikeid: bike id
      starttime, endtime, duration,
      name="", weather="", effort=0, notes=""
    Returns:
      ride: a ride
    """
    import gear_api
    cur_user = get_current_user()
    if not cur_user:
        return {"status": "error",
                "message": "please login first",
                }

    result = get_by_id(request, routeid)
    if result["status"] != "ok":
        return result
    route = result["route"]

    result = gear_api.get_bike_by_id(request, bikeid)
    if result["status"] != "ok":
        return result
    bike = result["bike"]

    if not isinstance(starttime, datetime):
        starttime = datetime.strptime(starttime, "%Y-%m-%d %H:%M")
    if not isinstance(endtime, datetime):
        endtime = datetime.strptime(endtime, "%Y-%m-%d %H:%M")

    duration = float(duration)

    delta = endtime-starttime
    if duration > (delta.days*24)+(delta.seconds/60.0):
        return {"status": "error",
                "message": "duration can not greater than end-start"}

    if not name:
        name = u"%s - %s"%(starttime.strftime("%Y-%m-%d"), route.name)

    try:
        ride = Ride.create(bike, cur_user, route, start_datetime=starttime, end_datetime=endtime, duration=duration,
                       name=name, weather=weather, effort=effort, notes=notes)
    except Exception,e:
        logging.exception("some error during log ride")
        return {"status": "error",
                "message": str(e)}

    return {"status": "ok",
            "ride": ride,
            "custom_encoder": _Ride_Encoder}

log_a_ride.escape = False

@check_site_user
def update_ride_notes(request, rideid, notes=""):
    """ log a ride
    Args:
      rideid: ride id
      notes: base64 encoded new ride notes
    Returns:
      ride: a ride
    """
    ride = Ride.get_by_id(long(rideid))
    if not ride:
        return {"status": "error",
                "message": "no ride %s"%rideid}

    cur_user = get_current_user()
    if ride.Rider != cur_user:
        return {"status": "error",
                "message": "you can not change other's notes"}

    try:
        ride.update_attr(notes=notes)
    except Exception,e:
        logging.exception("some error during log ride")
        return {"status": "error",
                "message": str(e)}

    return {"status": "ok",
            "ride": ride,
            "custom_encoder": _Ride_Encoder}

update_ride_notes.escape = False

def get_rides_by_routeid(request, routeid, pagesize=20, start_cursor=None, order='-create_datetime'):
    """return rides by routeid.
    Args:
      routeid: route's id
      pagesize: size of results
      start_cursor: start cursor positon for this search
      order: ordering for the results
    Returns:
      end_cursor: cursor position for next search,
      rides: list(ride), list of json serialized rides.
    """
    routeid = long(routeid)
    route = Route.get_by_id(routeid)
    if not route:
        return {"status": "error",
                 "rides": [],
                 "message": "can not found route %s"%routeid,
                 }

    pagesize = long(pagesize)
    rides,end_cursor = Ride.get_by_route(route, pagesize, start_cursor, order)
    return {"status": "ok",
             "end_cursor": end_cursor,
             "rides": rides,
             "custom_encoder": _Ride_Encoder}

def get_rides_by_uid(request, uid, pagesize=20, start_cursor=None, order='-start_datetime'):
    """return rides by uid.
    Args:
      uid: rider's uid
      pagesize: size of results
      start_cursor: start cursor positon for this search
      order: ordering for the results
    Returns:
      end_cursor: cursor position for next search,
      rides: list(ride), list of json serialized rides.
    """
    rider = people_api.get_user_by_uid(uid)
    if not rider:
        return {"status": "error",
                 "rides": [],
                 "message": "can not found user %s"%uid,
                 }

    pagesize = long(pagesize)
    rides,end_cursor = Ride.get_by_rider(rider, pagesize, start_cursor, order)
    return {"status": "ok",
             "end_cursor": end_cursor,
             "rides": rides,
             "custom_encoder": _Ride_Encoder}

def get_all_rides(request, pagesize=50, start_cursor=None, order='-create_datetime'):
    """return all rides.
    Args:
      pagesize: size of results
      start_cursor: start cursor positon for this search
      orders: ordering for the results
    Returns:
      end_cursor: cursor position for next search,
      rides: list(ride), list of json serialized rides.
    """
    pagesize = long(pagesize)
    rides,end_cursor = Ride.get_all(pagesize, start_cursor, order)
    return {"status": "ok",
             "end_cursor": end_cursor,
             "rides": rides,
             "custom_encoder": _Ride_Encoder}

def get_ride_by_id(request, rideid):
    """return ride by id.
    Args:
      rideid: rideid
    Returns:
      ride: json serialized ride.
    """
    ride = Ride.get_by_id(long(rideid))
    if ride:
        return {"status": "ok",
             "ride": ride,
             "custom_encoder": _Ride_Encoder}
    else:
        return {"status": "error",
             "message": "no ride %s"%rideid}

def search_routes(request, search_text, pagesize=50, start_cursor=None, order="-distance"):
    """full text search for routes.
    Args:
      search_text: search words
      pagesize: size of results
      start_cursor: start cursor positon for this search
      orders: ordering for the results
    Returns:
      end_cursor: cursor position for next search,
      routes: list(route), list of json serialized route.
    """
    pagesize = long(pagesize)
    routes,end_cursor = Route.search_by_text(search_text, pagesize=pagesize,
        start_cursor=start_cursor, order=order)
    return {"status": "ok",
            "end_cursor": end_cursor,
            "routes": routes,
            "custom_encoder": _Route_Encoder}

def search_comments(request, search_text, pagesize=50, start_cursor=None, order="-posted_on"):
    """full text search for comments.
    Args:
      search_text: search words
      pagesize: size of results
      start_cursor: start cursor positon for this search
      orders: ordering for the results
    Returns:
      end_cursor: cursor position for next search,
      comments: list(comment), list of json serialized comment.
    """
    pagesize = long(pagesize)
    comments,end_cursor = Comment.search_by_text(search_text, pagesize=pagesize,
        start_cursor=start_cursor, order=order)
    return {"status": "ok",
            "end_cursor": end_cursor,
            "comments": comments,
            "custom_encoder": _Comment_Encoder}

def main():
    pass

if __name__ == '__main__':
    main()
